grammar Cuda;

options {
  language = Java;
  output=AST;
}

tokens {
	STMT_SEQ;
	NEGATION;
  	CALL;
  	LIST;
  	DLIST;
}

@header {
  package cuda;
}

@lexer::header {
  package cuda;
}

@rulecatch {
	catch(RecognitionException e) {
		throw e;
	} 
}

program
	:	statementSequence
	;
	
statementSequence
	:	( statement (',' | ';')? )* -> ^(STMT_SEQ statement*)
	;

/**************** STATEMENTS **********************/
statement
	:	declarationStatement
	|	assignmentStatement
	|	ifStatement
	|	forStatement
	|	whileStatement
	|	switchStatement
	|	'break'
	|	'continue'
	;
	 
//TODO: Deklaracja kilku pod rzad?
declarationStatement
	:	'scalar'^ IDENT ('='! r_value)? /* Zwraca (scalar id val?) */
	|	'vector'^ IDENT ('['! expression ']'! | '='! r_value)? /* Zwraca (vector id dim val?) */
	|	'matrix'^ IDENT ('['! expression ']'! '['! expression ']'! | '='! r_value)? /* Zwraca (matrix id dim1 dim2 val?) */
	;

/**	Regula dla postawien. W szczegolnosci, dzieki opcjonalnej drugie czesci, regula ta umozliwia samo wywolywanie funkcji 
*	Jesli jest to wywolanie, to drzewo AST bedzie takie, jak przy wywolaniach (patrz call)
*	Jesli to jest przypisanie, drzewo AST ma postac:
*	(= l_value r_value)
*/
assignmentStatement
	:	l_value ('='^ r_value)?
	;	
	
/**	Regula dla wyrazen if.
*	Budowane drzewo AST ma postac:
*	(if expr stmt_seq (elseif expr stmt_seq)* (else stmt_seq)?) 
*/
ifStatement
	:	'if' if_e=expression
			if_s=statementSequence
		('elseif' elseif_e=expression
			elseif_s=statementSequence
		)* 
		('else'
			else_s=statementSequence
		)?
		'end'
		-> ^('if' $if_e $if_s ^('elseif' $elseif_e $elseif_s)* ^('else' $else_s)?)
	;
	
/**	Regula dla petli for
*	Budowane drzewo AST ma postac:
*	(for l_value r_value stmt_seq)	
*	r_value jest tutaj tak naprawde lista np. 1:1:10
*/
forStatement
	:	'for'^ l_value '='! r_value
			 statementSequence
		'end'!
	;
	
/**	Regula dla petli while.
*	Budowane drzewo ma postac:
*	(while expr stmt_seq)
*/
whileStatement
	:	'while'^ expression
			statementSequence
		'end'!
	;
	
/**	Regula dla wyrazen switch.
*	Budowane drzewo AST ma postac:
*	(switch expr (case expr+ stmt_seq)* (otherwise stmt_seq)?) 
*/
switchStatement
	:	'switch' s_e=expression
		('case' (c_e1=expression | ( '{' c_e1 = expression (',' c_e2=expression)* '}') )
			s1=statementSequence 
		)*
		('otherwise'
			s2=statementSequence
		)?
		'end'
		-> ^('switch' $s_e ^('case' $c_e1 $c_e2* $s1)* ^('otherwise' $s2)?)
	;
	
	// TODO: 
		// function definition - jak to zrobic, w matlabie robi sie w osobnych plikach 
	
/******************* VALUES ***********************/
/* Ponizsze reguly opisuja rozne rodzaje typow, wystepujacych w wyrazeniach */

/*	Odwolania do vectorow i macierzy maja taka sama skladnie jak wywolania funkcji, np. A(2,3) 
*	Zwracane dla nich drzewo AST ma postac (CALL IDENT arg+)
*/
l_value
	:	IDENT
		( ('(') => '(' argumentList ')' -> ^(CALL IDENT argumentList)	
		|	-> IDENT	
		)
	;
	
/**	Moga to byc zwykle wyrazenia, albo listy w postaci np 1:2, 1:x:3.
*	Dla list zwracane sa odpowiednio drzewa AST:
*	(LIST 1 2)
*	(DLIST 1 2 3)
*/
r_value
	:	e1=expression
		(
			(c1=':' e2=expression
				(c2=':' e3=expression -> ^(LIST $e1 $e2 $e3)
				|	-> ^(LIST $e1 $e2)
				)
			)
		|	-> $e1
		)
	;

argumentList
	:	expression  (','! expression)*
	;
		
scalar
	: 	INTEGER
	|	FLOAT
	;
	
term
	:	l_value
	|	scalar
	|	'('! expression ')'!
	;
		
/******************* EXPRESSIONS ***********************/
/*	Ponizsze reguly zwracaja klasyczne drzewa syntaktyczne dla wyrazen arytmetyczno-logicznych
*	Korzeniami podrzew sa same operatory, z wyjatkiem minusa unarnego, gdzie korzeniem jest
*	wirtualny token NEGATION.
*
*	Obslugiwane operatory:
*		* arytmetyczne: ^, .^, unarny -, unarny +, *, .*, /, ./, +, -
*		* relacyjne: ==, ~=, <, <=, >, >=
*		* logiczne: &&, ||, ~
*
*	Opracowano na podstawie http://www.mathworks.com/access/helpdesk/help/techdoc/matlab_prog/f0-40063.html
*
*	Uwaga: A.+B jest traktowane jako (.+ A B), natomiast w przypadku skalarow
*	2.+B jest traktowane jako (+ 2.0 B). Nie jest to jednak ograniczenie, gdyz w przypadku skalarow
*	operacje + i .+ sa tozsame.
*/

/* 	Troche bardziej skomplikowana regula. Buduje prawostronne drzewa, np. (^ 2 (^ 2 2)),
*	poniewaz tak wiaze sie potegowanie.
*	Pozostale reguly buduja lewostronne drzewa, np. (+ (+ 2 2) 2)
*/
power
	: term (('^'^ | '.^'^) power)?
	;	

//TODO: Czy dopuscic kilka pod rzad? Po co?
unary 
	:	('+'! | negation^ | '~'^)? power
	;
	
negation
	:	op='-' -> NEGATION[$op]
	;

mult
	:	unary (('*'^| '/'^ | '.*'^| './'^) unary)*  
	;	
	
add
	: 	mult ( ('+'^ | '-'^) mult)*
	;
	
relation
	:	add ( ('=='^ | '~='^ | '<'^ | '<='^ | '>'^  | '>='^) add)*
	;
	
//TODO: Czy zaimplementowac operatory '&' i '|', ktore operuja na el. nieskalarnych i generuja wynik ?
expression
	:	relation ( ('&&'^ | '||'^) relation)*
	;
	
/**************** TOKENS ********************/
	
fragment Digit : '0'..'9';
fragment Letter : 'a'..'z' | 'A'..'Z';
FLOAT: Digit+ '.' Digit*;
INTEGER: Digit+;
IDENT: Letter (Letter | Digit|'_')*;
WS: (' ' | '\t' | '\r' | '\n')+ {$channel = HIDDEN;};

//TODO: komentarze matlaba %( %), %
